package com.dungeon.game;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import android.graphics.Color;

import com.dungeon.game.Constants.Character_action_code;
import com.dungeon.game.Constants.Class_code;
import com.dungeon.game.Constants.Dungeon_event_code;
import com.dungeon.game.Constants.Equipment_type_code;
import com.dungeon.game.Constants.Monster_code;
import com.dungeon.game.Constants.Position_code;
import com.dungeon.game.Constants.Stance_code;
import com.dungeon.game.Constants.Status_effect_code;
import com.dungeon.game.Enemies.Mob;
import com.dungeon.game.Party.Character;
import com.dungeon.game.Scenario.ScenarioEvent;

public class World implements Serializable{

	double clock;
	Random rand;
	int collected_gold, waiting_gold, removed_gold, n_fled, event_no, darkness;
	ArrayList<Double> evil_kills;
	ArrayList<Equipment_type_code> items_found;
	ArrayList<Monster_code> mob_queue;
	boolean clear, can_flee, party_fleeing, traps_here, no_fly, mobs_will_leave, dungeon_won, resetting, accelerate_fleeing;
	double mobs_arrived, begin, now, since_last_process, clear_as_of, start_fleeing_at, started_pause_at, started_respite_at, darkness_at, silence_until, slow_time_until, poison_cloud_until, end_at;
	boolean waiting_for_chest_noise, waiting_for_chest_contents, waiting_for_some_reason, chest_successful, waiting_for_flying, fast_time;
	double chest_noise_at, chest_action_at, continue_at, flying_until;
	String chest_noise;
	int chest_gold_amount, chest_gems_amount;
	Mob poison_cloud_from;
	List<ScenarioEvent> scenario;
	double pos_record_updated_at;
	Position_code chain_lightning_pos;
	double chain_lightning_last_time;
	int chain_lightning_damage;
	boolean can_bribe, can_fall, can_sneak;
	double last_heal, last_char_death;
	int last_heal_amount;
	Character last_heal_target;
	Position_code boulder_pos;
	double boulder_dropped;
	boolean boulder_flash;
	boolean door_pick, door_bash, door_knock, waiting_for_door;
	double door_action_at, blast_at, boom_at;
	boolean earth_elemental_gone;
	double earth_elemental_returns;
	int earth_elemental_health;
	boolean can_pray;
	ArrayList<Double> meteors_at;
	double lich_summon_at;
	double lich_flee_at;
	int lich_health;
	boolean tutorial_mode;
	ArrayList<Double> party_damage_times, mob_damage_times;
	ArrayList<Integer> party_damage_amounts, mob_damage_amounts;
	double dark_flare, bright_flare;
	Position_code show_flash_image_in_party_pos;
	String show_flash_image_in_party_image;
	double show_flash_image_in_party_until;

	public World(){
		initialise();
		scenario = null;
	}

	void reset(Scenario _scenario){
	    initialise();
	    scenario = _scenario.make();
	}

	void initialise(){
		rand = new Random();
		evil_kills = new ArrayList<Double>();
		begin = 0;
	    now = 0;
	    since_last_process = begin;
	    clock = 0;
	    can_flee = true;
	    party_fleeing = false;
	    start_fleeing_at = -1;
	    accelerate_fleeing = false;
	    n_fled = 0;
	    waiting_gold = 0;
	    clear = true;
	    clear_as_of = begin;
	    collected_gold = 0;
	    removed_gold = 0;
	    items_found = new ArrayList<Equipment_type_code>();
	    event_no = 0;
	    started_pause_at = -1;
	    started_respite_at = -1;
	    traps_here = false;
	    no_fly = false;
	    mobs_will_leave = false;
	    mobs_arrived = -1;
	    dungeon_won = false;
	    darkness_at = -1;
	    darkness = 0;
	    silence_until = 0;
	    slow_time_until = 0;
	    poison_cloud_until = -1;
	    poison_cloud_from = null;
	    mob_queue = new ArrayList<Monster_code>();
	    resetting = false;
	    end_at = -1;
	    waiting_for_chest_noise = false;
	    waiting_for_chest_contents = false;
	    waiting_for_some_reason = false;
	    waiting_for_flying = false;
	    pos_record_updated_at = 1;
		chain_lightning_pos = null;
		fast_time = false;
		can_bribe = true;
		last_heal = 0;
		last_heal_amount = 0;
		last_heal_target = null;
		last_char_death = 0;
		boulder_pos = null;
		boulder_dropped = 0;
		boulder_flash = false;
    	door_bash = false;
    	door_pick = false;
    	door_knock = false;
        waiting_for_door = false;
        door_action_at = 0;
        can_fall = false;
        can_sneak = true;
    	earth_elemental_gone = false;
    	earth_elemental_returns = 0.0;
    	earth_elemental_health = 0;
    	can_pray = true;
    	blast_at = 0;
    	boom_at = 0;
    	meteors_at = new ArrayList<Double>();
    	lich_summon_at = 0;
    	lich_flee_at = 0;
    	lich_health = 0;
    	tutorial_mode = false;
    	party_damage_times = new ArrayList<Double>();
    	mob_damage_times = new ArrayList<Double>();
    	party_damage_amounts = new ArrayList<Integer>();
    	mob_damage_amounts = new ArrayList<Integer>();
    	dark_flare = 0;
    	bright_flare = 0;
		show_flash_image_in_party_pos = null;
		show_flash_image_in_party_image = "";
		show_flash_image_in_party_until = 0;
		if (Campaignstate.difficulty_level==1) Constants.TIME_SCALAR = 0.8;
    	if (Campaignstate.difficulty_level==2) Constants.TIME_SCALAR = 1.0;
    	if (Campaignstate.difficulty_level==3) Constants.TIME_SCALAR = 1.25;
    }

	int reflex_chance(int reflexes, int level, int action_reflex_level){
	    int result = 20 + (level + (reflexes-3) - action_reflex_level) * 15;
	    return Math.max(5,Math.min(result,success_chance(reflexes + 3)));
	}

	double cost_mult(int stamina){
		double result = 0;
	    if (stamina==1)
	        result = 2;
	    if (stamina==2)
	        result = 1.6;
	    if (stamina==3)
	        result = 1.2;
	    if (stamina==4)
	        result = 1;
	    if (stamina==5)
	        result = 0.85;
	    if (stamina==6)
	        result = 0.7;
	    if (stamina==7)
	        result = 0.65;
	    return result;
	}

		void find_gold(int _n){
			if (_n<=0)
				return;
		    Gamestate.add_sound("gold");
		    double increase = 1.0;
		    for (Character c : Gamestate.p.chars){
		        if (c.alive && !c.about_to_die){
		            for (Character_action a : c.actions){
		                if (a.action_code==Character_action_code.CA_MORE_GOLD){
		                    increase = increase + (a.increase()-1);
		                }
		            }
		        }
		    }
		    int n = (int)(Math.round(_n * increase));
		    Gamestate.add_message("Found "+(n),"gold",Color.WHITE);
		    collected_gold = collected_gold + n;
		    int energy_gain = (int)(Math.floor(0.2 * n));
		    int n_awarded = 0;
		    boolean failed = false;
		    while (n_awarded < energy_gain && !failed){
		        failed = true;
		        for (Character c : Gamestate.p.chars){
		            if (c.alive && !c.about_to_die && c.energy < c.max_energy && c.energy_for_gold && n_awarded < energy_gain){
		                c.energy = c.energy + 1;
		                n_awarded = n_awarded + 1;
		                failed = false;
		            }
		        }
		    }
		}

		void find_gems(int _n){
		    Gamestate.add_sound("gems");
		    double increase = 1.0;
		    for (Character c : Gamestate.p.chars){
		        if (c.alive && !c.about_to_die){
		            for (Character_action a : c.actions){
		                if (a.action_code==Character_action_code.CA_MORE_GEMS){
		                    increase = increase + (a.increase()-1);
		                }
		            }
		        }
		    }
		    int n = (int)(Math.round(_n * increase));
		    Gamestate.add_message("Found "+(n),"gems",Color.WHITE);
		    collected_gold = collected_gold + n;
		}

		void process(double ms){
		    // How much real time has elapsed
			double elapsed_since_last_process;
			double speed = Constants.TIME_SCALAR;
		    if (slow_time_until > clock)
		        speed = speed * 0.5;
		    if (fast_time)
		        speed = speed * 4;
		    for (Mob m : Gamestate.e.mobs){
		    	if (m.alive && !m.about_to_die && m.speeds_time){
		    		if (m.front_row())
		    			speed = speed * 3;
		    		else
		    			speed = speed * 2;
		    	}
		    }
	        elapsed_since_last_process = speed * ms/1000.0;
		    clock = clock + elapsed_since_last_process;
		    if (elapsed_since_last_process==0)
		        return;
		    // The only time variables that should be used below here are elapsed_since_last_process && clock
		    if (Gamestate.quit_after_pause)
		    	if (end_at<0)
		    		end_at = clock + 3;
		    // Keep a track of where the characters are
		    if (clock > (pos_record_updated_at+1)){
		    	pos_record_updated_at = clock;
		    	for (Character c : Gamestate.p.chars){
		    		if (c.alive && !c.about_to_die){
		    			if (c.pos==Position_code.POS_C_BACK_1 || c.pos==Position_code.POS_C_BACK_2 || c.pos==Position_code.POS_C_BACK_3 || c.pos==Position_code.POS_C_FRONT_1 || c.pos==Position_code.POS_C_FRONT_2 || c.pos==Position_code.POS_C_FRONT_3)
		    				c.pos_record.add(c.pos);
		    		}
		    	}
		    }
		    // World effects;
		    if (can_fall && Gamestate.p.n_surviving_chars()>1){
		    	for (Character c : Gamestate.p.chars){
		    		if (c.alive && !c.about_to_die){
						if ((c.has_status_effect(Status_effect_code.EF_STUN) && one_in((int)Math.floor(3.0 / elapsed_since_last_process))) || (party_fleeing && one_in((int)Math.floor(9.0 / elapsed_since_last_process)))){
			    			Gamestate.add_message("Fell off the crag!",c.image,Color.WHITE);
			    			Gamestate.add_sound("falling");
			                c.slide_from = c.pos;
			                c.slide_to = Position_code.POS_DOWN;
			                c.slide_start = Gamestate.w.clock;
			                c.slide_finish = Gamestate.w.clock + 0.15;
		    	            c.pos = null;
		    	            c.alive = false;
		    	            c.about_to_die = false;
			    	        Gamestate.p.after_character_leaves(false);
	    	                break;
		    			}
		    		}
		    	}
		    }
		    if (meteors_at.size()==0){
			    if (boulder_dropped>0 && boulder_flash==false && Gamestate.w.clock>boulder_dropped+0.0 && Gamestate.w.clock<boulder_dropped+0.3){
			    	boulder_flash = true;
			    } else if (boulder_dropped>0 && boulder_flash==false && Gamestate.w.clock>boulder_dropped+1.0 && Gamestate.w.clock<boulder_dropped+1.3){
			    	boulder_flash = true;
			    } else if (boulder_dropped>0 && boulder_flash==false && Gamestate.w.clock>boulder_dropped+2.0 && Gamestate.w.clock<boulder_dropped+2.3){
			    	boulder_flash = true;
			    } else if (boulder_dropped>0 && boulder_flash==true && Gamestate.w.clock>boulder_dropped+0.3 && Gamestate.w.clock<boulder_dropped+1.0){
			    	boulder_flash = false;
			    } else if (boulder_dropped>0 && boulder_flash==true && Gamestate.w.clock>boulder_dropped+1.3 && Gamestate.w.clock<boulder_dropped+2.0){
			    	boulder_flash = false;
			    } else if (boulder_dropped>0 && boulder_flash==true && Gamestate.w.clock>boulder_dropped+2.3 && Gamestate.w.clock<boulder_dropped+3.0){
			    	boulder_flash = false;
			    } else if (boulder_dropped>0 && boulder_dropped<Gamestate.w.clock-3.0){
			    	boulder_dropped=0;
			    	Character c = Gamestate.p.get_char_at_pos(boulder_pos);
			    	if (c!=null && c.alive && !c.about_to_die){
			    		if (c.current_stance!=null & c.current_stance==Stance_code.ST_LEAP_ASIDE){
			    			Gamestate.add_message("Dodged the rock!",c.image,Color.WHITE);
			    			c.current_stance=null;
			    		} else if (c.current_stance!=null & c.current_stance==Stance_code.ST_CATCH){
			    			Gamestate.add_message("Caught the rock!",c.image,Color.WHITE);
				    		c.take_damage(Gamestate.w.randint(8, 12));
			    			c.current_stance=null;
			    		} else {
			    			Gamestate.add_sound("thump");
			    			Gamestate.add_message("Hit by a rock!",c.image,Color.WHITE);
				    		c.take_damage(Gamestate.w.randint(50, 100));
			                c.flash_image="boulder";
			                c.flash_long = true;
			    		}
			    	} else {
			    		show_flash_image_in_party_pos = boulder_pos;
			    		show_flash_image_in_party_image="boulder";
			    		show_flash_image_in_party_until = clock+0.5;
			    	}
			    }
		    }
		    if (lich_flee_at>0 && clock>lich_flee_at){
		    	lich_flee_at = 0;
		    	for (Mob m : Gamestate.e.mobs){
		    		if (m.code==Monster_code.MOB_UNDEAD_BOSS_PHASE12 || m.code==Monster_code.MOB_UNDEAD_BOSS_PHASE3){
				    	lich_health = m.health;
		    			Gamestate.add_message("'Time to make an exit!'",m.image,Color.WHITE);
		    			m.flee();
		    		}
		    	}
		    }
		    if (lich_summon_at>0 && clock>lich_summon_at){
		    	lich_summon_at = 0;
                Gamestate.add_message("'Fight my minions!'","lich2",Color.WHITE);
                Gamestate.add_sound("summon");
                for (Monster_code type : Arrays.asList(Monster_code.MOB_GIANT_SKELETON,Monster_code.MOB_SKELETON,Monster_code.MOB_SKELETON,Monster_code.MOB_BURNING_GHOST,Monster_code.MOB_MUMMY,Monster_code.MOB_SKELETON,Monster_code.MOB_SKELETON,Monster_code.MOB_SKELETON)){
	                Mob m = Gamestate.e.add_mob(type);
	                if (m==null) {
	                    mob_queue.add(type);
	                }
                }
		    }
		    if (meteors_at.size()>0 && meteors_at.get(0)<clock){
		    	boolean lich_still_here = false;
		    	for (Mob m : Gamestate.e.mobs){
		    		if ((m.code==Monster_code.MOB_UNDEAD_BOSS_PHASE12 || m.code==Monster_code.MOB_UNDEAD_BOSS_PHASE3 || m.code==Monster_code.MOB_UNDEAD_BOSS_PHASE4) && m.alive && !m.about_to_die){
		    			lich_still_here = true;
		    		}
		    	}
		    	if (lich_still_here){
			    	if (boulder_dropped==0){
			    		boulder_dropped = meteors_at.get(0);
			    		boulder_pos = Position_code.POS_C_FRONT_2;
			        	Character tgt = Gamestate.p.random_target(null);
			        	if (tgt!=null)
			        		boulder_pos = tgt.pos;
			    	}
				    if (boulder_dropped>0 && boulder_flash==false && Gamestate.w.clock>boulder_dropped+0.0 && Gamestate.w.clock<boulder_dropped+0.3){
				    	boulder_flash = true;
				    } else if (boulder_dropped>0 && boulder_flash==false && Gamestate.w.clock>boulder_dropped+1.0 && Gamestate.w.clock<boulder_dropped+1.3){
				    	boulder_flash = true;
				    } else if (boulder_dropped>0 && boulder_flash==false && Gamestate.w.clock>boulder_dropped+2.0 && Gamestate.w.clock<boulder_dropped+2.3){
				    	boulder_flash = true;
				    } else if (boulder_dropped>0 && boulder_flash==true && Gamestate.w.clock>boulder_dropped+0.3 && Gamestate.w.clock<boulder_dropped+1.0){
				    	boulder_flash = false;
				    } else if (boulder_dropped>0 && boulder_flash==true && Gamestate.w.clock>boulder_dropped+1.3 && Gamestate.w.clock<boulder_dropped+2.0){
				    	boulder_flash = false;
				    } else if (boulder_dropped>0 && boulder_flash==true && Gamestate.w.clock>boulder_dropped+2.3 && Gamestate.w.clock<boulder_dropped+3.0){
				    	boulder_flash = false;
				    } else if (boulder_dropped>0 && boulder_dropped<Gamestate.w.clock-3.0){
				    	Character c = Gamestate.p.get_char_at_pos(boulder_pos);
					    Gamestate.add_sound("fire_bolt");
				    	if (c!=null && c.alive && !c.about_to_die){
				    		if (c.current_stance!=null & c.current_stance==Stance_code.ST_LEAP_ASIDE){
				    			Gamestate.add_message("Dodged the meteor!",c.image,Color.WHITE);
				    			c.current_stance=null;
				    		} else {
				    			Gamestate.add_sound("thump");
				    			Gamestate.add_message("Hit by a meteor!",c.image,Color.WHITE);
					    		c.take_damage(Gamestate.w.randint(50, 100));
				                c.flash_image="meteor";
				                c.flash_long = true;
				    		}
				    	}
				    	for (Position_code p : Arrays.asList(Position_code.POS_C_BACK_1,Position_code.POS_C_BACK_2,Position_code.POS_C_BACK_3,Position_code.POS_C_FRONT_1,Position_code.POS_C_FRONT_2,Position_code.POS_C_FRONT_3)){
				    		if (p.close(boulder_pos) || p.equals(boulder_pos)){
				    			Character cc = Gamestate.p.get_char_at_pos(p);
				    			if (cc!=null && cc.alive && !cc.about_to_die){
				    			   int damage = randint(6,18);
			    	               if (cc.current_stance==Stance_code.ST_TAKE_COVER){
			    	                   damage = damage / 3;
			    	                   cc.current_stance = null;
			    	               }
		    	            	   if (cc.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
		    	                       damage = damage / 4;
		    	                   else if (cc.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || cc.fire_resistance)
		    	                       damage = damage / 2;
			    	               cc.take_damage(damage);
			    	               if (!p.equals(boulder_pos)){
			    	            	   cc.flash_image = "fire_down";
						               cc.flash_long = true;
			    	               }
				    			}
				    		} else {
					    		show_flash_image_in_party_pos = boulder_pos;
					    		show_flash_image_in_party_image="meteor";
					    		show_flash_image_in_party_until = clock+0.5;
					    	}
				    	}
				    	meteors_at.remove(meteors_at.get(0));
				    	boulder_dropped=0;
				    	boulder_pos = null;
				    }
		    	}
		    }
		    if (blast_at>0 && clock>blast_at){
		    	blast_at = 0;
    			Gamestate.add_message("There is a sudden explosion!","fire_trap",Color.WHITE);
		        Gamestate.add_sound("breath");
		        for (Mob m : Gamestate.e.mobs){
		            if (m.alive && !m.about_to_die && !m.fire_resistant){
		                m.flash_image = "fire";
		                m.take_damage(30);
		            }
		        }
		    }
		    if (boom_at>0 && clock>boom_at){
		    	boom_at = 0;
    			Gamestate.add_message("The flask explodes!","boom_flask",Color.WHITE);
		        Gamestate.add_sound("bang");
		        if (one_in(4)){
			        for (Mob m : Gamestate.e.mobs){
			            if (m.alive && !m.about_to_die && !m.fire_resistant){
			                m.flash_image = "fire";
			                m.take_damage(randint(10,20));
			            }
			        }
		        } else {
			        for (Character c : Gamestate.p.chars){
			            if (c.alive && !c.about_to_die){
			                c.flash_image = "fire";
			                int damage = randint(10,20);
			                if (c.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
			                    damage = damage / 4;
			                else if (c.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || c.fire_resistance)
			                    damage = damage / 2;
			                c.take_damage(damage);
			            }
			        }
		        }
		    }
		    if (Gamestate.w.earth_elemental_gone && Gamestate.w.earth_elemental_returns<Gamestate.w.clock){
		    	if (Gamestate.e.n_surviving_mobs()==6){
		    		for (Mob m : Gamestate.e.mobs){
		    			if (m.alive && m.about_to_die && m.level<8 && !m.never_encountered_randomly){
		    				m.flee();
		    				break;
		    			}
		    		}
		    	}
		    	if (Gamestate.e.n_surviving_mobs()<6){
			    	Mob new_elemental = Gamestate.e.add_mob(Monster_code.MOB_EE);
			    	if (new_elemental!=null){
			    		Gamestate.add_sound("submerge");
				    	Gamestate.w.earth_elemental_gone = false;
			    		new_elemental.health = Gamestate.w.earth_elemental_health;
			    	}
		    	}
		    }
		    for (Character c : Gamestate.p.chars){
		    	if (c.alive && !c.about_to_die && c.n_balm_remaining>0 && c.next_balm_at<clock){
		    		c.n_balm_remaining = c.n_balm_remaining - 1;
		    		c.next_balm_at = c.next_balm_at + 0.7;
		    		if (c.health < c.max_health){
		    			c.health = c.health + 1;
		    		}
		    	}
		    }
		    for (Character c : Gamestate.p.chars){
		    	if (c.alive && !c.about_to_die && c.drowning && c.drowning_from < Gamestate.w.clock - 60){
		    		if (one_in((int)Math.floor(0.25 / elapsed_since_last_process))){
		    			c.take_damage(1);
		    		}
		    	}
		    }
		    if (darkness_at>0){
		        if ((darkness > 3 && darkness_at < (clock - 2.0)) || (darkness > 0 && darkness_at < (clock - 5.0))){
		            if (darkness<=1){
		                darkness = 0;
		                darkness_at = -1;
		            } else {
		                darkness = darkness - 1;
		                darkness_at = clock;
		            }
		        }
		    }
		    if (chain_lightning_pos!=null && clock>chain_lightning_last_time+0.5){
		    	if (randint(1,100)<=63){
		    		int count = 0;
		    		boolean ok = false;
		    		while (count <= 10){
		    			count = count + 1;
		    			Mob this_mob = Gamestate.e.random_target(null);
		    			if (this_mob!=null && this_mob.alive && !this_mob.about_to_die && !this_mob.lightning_resistant && this_mob.pos!=chain_lightning_pos){
			    			chain_lightning_pos = this_mob.pos;
				    		chain_lightning_last_time = clock;
			    			ok = true;
			    			Gamestate.add_sound("lightning_bolt");
			    			this_mob.take_damage(chain_lightning_damage);
			    			this_mob.flash_image = "lightning";
			    			break;
		               }
		    		}
		    		if (!ok)
		    			chain_lightning_pos = null;
		    	} else {
		    		chain_lightning_pos = null;
		    	}
		    }
		    if (poison_cloud_until > clock){
		        Mob caster = poison_cloud_from;
		        if (caster!=null && !caster.alive){
		            poison_cloud_until = 0;
		        } else {
		            for (Character c : Gamestate.p.chars){
		                if (c.alive && !c.about_to_die && c.front_row()){
							double period = 1.0;
							if (poison_cloud_from!=null && poison_cloud_from.code==Monster_code.MOB_YOUNG_GREEN_DRAGON) period = period/4.0;
		                    if (one_in((int)Math.floor(period / elapsed_since_last_process))){
		                        if (c.has_status_effect(Status_effect_code.EF_RESIST_POISON) ||  c.poison_resistance){
		                            if (one_in(3)){
		                                c.take_damage(1);
		                                c.flash_image = "poison_cloud";
		                            }
		                        } else {
		                            c.take_damage(1);
		                            c.flash_image = "poison_cloud";
		                            int poison_level;
		                            if (!c.has_status_effect(Status_effect_code.EF_POISON)){
		                                poison_level = 1;
		                            } else {
		                                poison_level = c.status_effects.get(Status_effect_code.EF_POISON).level;
		                            }
		                            c.status_effects.put(Status_effect_code.EF_POISON,new Effect_parameters(10));
		                            c.status_effects.get(Status_effect_code.EF_POISON).level = poison_level;
		                        }
		                    }
		                }
		            }
		        }
		    }
		    // Unshorten the time
		    if (clear && fast_time)
		    	fast_time = false;
		    // Hand out money if all the mobs are dead
		    if (clear && waiting_gold>0 && !party_fleeing){
		        if (Gamestate.e.n_surviving_mobs()>0){
		            clear = false;
		        } else if (clock > clear_as_of + 0.1){
		            find_gold(waiting_gold);
		            waiting_gold = 0;
		        }
		    } else if (clear && party_fleeing && !accelerate_fleeing){
		    	accelerate_fleeing = true;
		    	start_fleeing_at = clock - 0.2 * n_fled;
		    }
		    if (party_fleeing){
		    	waiting_for_chest_contents = false;
		    	waiting_for_chest_noise = false;
		    	waiting_for_door = false;
		    	waiting_for_flying = false;
		    	waiting_for_chest_noise = false;
		    }
		    // any new mobs waiting to arrive?
		    while (Gamestate.e.n_mobs_taking_up_space()<6 && mob_queue.size()>0){
		        Monster_code mob_code = mob_queue.get(mob_queue.size()-1);
		        mob_queue.remove(mob_queue.size() - 1);
		        Gamestate.e.add_mob(mob_code);
		        clear = false;
		    }
		    // now loop through characters
            for (Character c : Gamestate.p.chars){
		        // any chars in the process of dying?
		        if (c.about_to_die && clock > c.die_at)
		            c.die();
		        // Everything below this, only if alive and not about to die
		        if (!(c.alive && !c.about_to_die ))
		        	continue;
		        // any stances wear off?
		        if (c.current_stance!=null){
		            if ((c.stance_wears_off>0 && c.stance_wears_off < clock) || (c.current_stance==Stance_code.ST_RETRENCH && c.energy==c.max_energy)){
		                if (c.current_stance==Stance_code.ST_BERSERK)
		                    Gamestate.add_message("Has calmed down...",c.image,Color.WHITE);
		                c.current_stance = null;
		            }
		        }
		        // escape paralysis?
		        if (c.has_status_effect(Status_effect_code.EF_GRAPPLE) && c.strength>2){
		        	double difficulty = 90;
		        	if (c.grappled_by.max_health > 150)
		        		difficulty = difficulty * 2;
		        	if (one_in((int)Math.floor((difficulty/(c.strength*c.strength)) / elapsed_since_last_process))){
		        		Gamestate.add_message("Breaks free!",c.image,Color.WHITE);
		        		c.status_effects.get(Status_effect_code.EF_GRAPPLE).lasts_until = 0;
		        	}
		        }
		        // no longer webbed?
		        if (c.webbed_until>0 && c.webbed_until<Gamestate.w.clock){
		        	c.webbed_until = 0;}
		        // having their life drained?
		        if (c.next_energy_drain<Gamestate.w.clock && c.next_energy_drain>0){
		        	if (c.energy<=0){
		        		c.next_energy_drain = 0;
		        	} else {
		        		c.energy = c.energy - 1;
		        		c.next_energy_drain = c.next_energy_drain + 0.4;
		        	}
		        }
		        // counter attack from a Riposte?
		        if (c.current_stance==Stance_code.ST_COUNTER){
		            Mob target = c.counter_against;
		            c.current_stance = null;
		            if (target!=null && target.alive && !target.about_to_die){
		               if (c.melee_hit_roll(target)){
		                    Gamestate.add_sound(c.melee_hit_sound);
		                    int damage = c.melee_damage_roll(target);
		                    int armor = target.current_armor();
		                    if (c.has_status_effect(Status_effect_code.EF_FIND_WEAKNESS))
		                        armor = Math.max(0,armor-5);
		                    if (damage > armor){
		                        c.flash_image = "pale";
		                        target.take_damage(damage-armor);
		                    }
		               }
		            }
		        }
		        // any delayed actions ready to resolve?
		        if (c.action_in_progress!=null){
		            if (c.action_fires>0 && c.action_fires < clock){
		            	int n_chars = Gamestate.p.chars.size();
		                c.action_in_progress.resolve();
		                c.action_in_progress = null;
		                if (Gamestate.p.chars.size() > n_chars)
		                	break; // Can happen with summon friend. Need to break out of the outer loop - no good to modify something we're iterating over
		            }
		        }
		        // anyone want to act on reflex?
		        if (c.dumb_until < clock && c.action_in_progress==null && c.current_stance==null && !dungeon_won){
		            if (!(c.has_status_effect(Status_effect_code.EF_PARALYSIS)) && !(c.has_status_effect(Status_effect_code.EF_PETRIFY)) && !(c.has_status_effect(Status_effect_code.EF_STUN)) && !(c.has_status_effect(Status_effect_code.EF_FEAR)) && !(c.has_status_effect(Status_effect_code.EF_GRAPPLE)) && !(c.has_status_effect(Status_effect_code.EF_SLEEP))){
		                for (Character_action a : c.actions){
		                    if (a.needed() && a.ok()){
		                        if (randint(1,100)<=reflex_chance(c.get_reflexes(),c.level,a.reflex_level())){
		                            Gamestate.add_message("Uses " + a.name(),c.image,Color.WHITE);
		                            a.set_reflex_target();
		                            a.trigger();
		                            c.flash_image = "verypale";
		                        } else {
		                            c.dumb_until = clock + 2;
		                        }
		                        break;
		                    }
		                }
		            }
		        }
		        // regenerate bar (or berserk warriors regenerate health instead)
		        if (c.energy_gain_period>0){
		            if (clock > c.last_energy_gain + c.energy_gain_period){
		                if (c.lose_health){
							if (randint(1,4)==1){
								c.take_damage(randint(1,3));
							}
						}
						if (!(c.has_status_effect(Status_effect_code.EF_STUN) || c.has_status_effect(Status_effect_code.EF_PETRIFY) || c.has_status_effect(Status_effect_code.EF_PARALYSIS) || c.current_stance==Stance_code.ST_BERSERK)){
		                    c.last_energy_gain = clock;
		                    int gain_amount = 1;
		                    if (c.current_stance==Stance_code.ST_RETRENCH)
		                        gain_amount = gain_amount * 3;
		                    c.energy = Math.min(c.max_energy,c.energy + gain_amount);
		                } else if (c.current_stance==Stance_code.ST_BERSERK){
		                    c.last_energy_gain = clock;
		                    c.health = Math.min(c.max_health, c.health+2);
		                }
		            }
		        }
		        // poison hurts
		        if (c.has_status_effect(Status_effect_code.EF_POISON) && c.status_effects.get(Status_effect_code.EF_POISON).level>0){
		            if (c.status_effects.get(Status_effect_code.EF_POISON).lasts_until <= (clock + 0.5)){
		                if (c.status_effects.get(Status_effect_code.EF_POISON).level>1)
		                    c.status_effects.get(Status_effect_code.EF_POISON).lasts_until = clock + 10;
		                c.status_effects.get(Status_effect_code.EF_POISON).level = c.status_effects.get(Status_effect_code.EF_POISON).level-1;
		            } else {
		                if (one_in((int)Math.floor((3.0/c.status_effects.get(Status_effect_code.EF_POISON).level) / elapsed_since_last_process)))
		                    c.take_damage(1);
		            }
		        }
		        if (!can_pray && c.class_code==Class_code.C_PRIEST){
		        	if (c.energy>0){
		        		if (one_in((int)Math.floor(0.5 / elapsed_since_last_process))){
		        			c.energy = c.energy - 1;
		        		}
		        	}
		        }
		        // run away!
		        if (c.has_status_effect(Status_effect_code.EF_FEAR) && c.can_act() && c.front_row()){
		            if (one_in((int)Math.floor(3.0 / elapsed_since_last_process))){
		                Gamestate.add_sound("fear");
		                Gamestate.add_message("Aaaaaargh!",c.image,Color.WHITE);
		                if (!Gamestate.p.move_character(c))
		                	Gamestate.p.move_character_to(c, (Arrays.asList(Position_code.POS_C_BACK_1,Position_code.POS_C_BACK_2,Position_code.POS_C_BACK_3)).get(randint(0,2)) );
		            }
		        }
		    }
            if (Gamestate.p.n_surviving_chars()>0){
			    for (int i=0; i<Gamestate.e.mobs.size(); i++){
					Mob m = Gamestate.e.mobs.get(i);
			        // any mobs in the process of dying?
			        if (m.about_to_die && clock > m.die_at)
			            m.die();
			        // any delayed mob actions ready to resolve?
			        if (m.alive && !m.about_to_die && m.action_in_progress!=null){
			            if (m.action_fires>0 && m.action_fires < clock){
			            	boolean no_valid_target = false;
			                if (m.action_in_progress.mob_target!=null){
			                    if (!m.action_in_progress.mob_valid_targets().contains(m.action_in_progress.mob_target)){
			                    	List<Mob> valid_targets = m.action_in_progress.mob_valid_targets();
			                        if (valid_targets.size()==0)
			                        	no_valid_target = true;
			                        else
				                        m.action_in_progress.mob_target = valid_targets.get(randint(0,valid_targets.size()-1));
			                    }
			                }
			                if (m.action_in_progress.char_target!=null){
			                	if (!m.action_in_progress.char_valid_targets().contains(m.action_in_progress.char_target)){
			                    	List<Character> valid_targets = m.action_in_progress.char_valid_targets();
			                        if (valid_targets.size()==0)
			                        	no_valid_target = true;
			                        else
				                        m.action_in_progress.char_target = valid_targets.get(randint(0,valid_targets.size()-1));
			                    }
			                }
			                if (!no_valid_target)
			                	m.action_in_progress.resolve();
			                m.action_in_progress.last_used = clock;
			                m.action_in_progress = null;
			            }
			        }
			    }
		    }
		    // what happens next in the scenario?
		    boolean must_pause = false, doing_something = false;
		    while (event_no < scenario.size() && must_pause==false && doing_something==false){
		        ScenarioEvent this_event = scenario.get(event_no);
		        // if we are flying, we may bypass this
		        boolean flying = false;
		        String image="";
		        for (Character c : Gamestate.p.chars){
		            if (c.alive && !c.about_to_die && c.current_stance==Stance_code.ST_FLY){
		                flying = true;
		                image = c.image;
		            }
		        }
		        if (flying && !waiting_for_flying){
		            if (this_event.code==Dungeon_event_code.DE_NOFLY || this_event.code==Dungeon_event_code.DE_ALIGHT || this_event.code==Dungeon_event_code.DE_QUIT){
		            	Gamestate.add_message("You rise into the air!",image,Color.WHITE);
		        		Gamestate.add_sound("fly");
		            	waiting_for_flying = true;
		            	flying_until = clock + 3.0;
		            	doing_something = true;
		            	continue;
		            } else {
		                started_pause_at = -1;
		                started_respite_at = -1;
		                event_no = event_no + 1;
		                continue;
		            }
		        }
		        // are we in the middle of a pause?
		        if (this_event.code==Dungeon_event_code.DE_PAUSE){
		            if (started_pause_at<0)
		                started_pause_at = clock;
		            if (started_respite_at<0 && Gamestate.e.n_surviving_mobs()==0 && !(earth_elemental_returns>clock))
		                started_respite_at = clock;
		            if (started_pause_at >= clock - this_event.pause_s)
		                if (started_respite_at<0 || this_event.respite_s==null || (started_respite_at >= clock - this_event.respite_s))
		                    must_pause = true;
		            if (must_pause==false){
		                started_pause_at = -1;
		                started_respite_at = -1;
		                event_no = event_no + 1;
		            }
		        } else if (waiting_for_door){
		        	if (door_action_at <= clock){
		        		waiting_for_door = false;
		        		if (door_pick){
			                Gamestate.add_message("You manage to pick the lock!","door",Color.WHITE);
			                Gamestate.add_sound("door");
		        		} else if (door_bash){
			                Gamestate.add_message("You manage to bash the door down!","door",Color.WHITE);
			                Gamestate.add_sound("breaking_door");
		        		} else if (door_knock){
			                Gamestate.add_message("You manage to open the door with magic!","door",Color.WHITE);
			                Gamestate.add_sound("good_spell");
		        		} else {
			                Gamestate.add_message("You cannot get it open! You will have to give up.","door",Color.WHITE);
			                Gamestate.add_sound("cancel");
			                int xp = 0;
			                for (Character c : Gamestate.p.chars){
			                    if (c.alive && !c.about_to_die)
			                        xp = xp + c.xp;
			                }
			                Gamestate.quit_after_pause = true;
			                Gamestate.add_message("Escaped with " + xp + " XP and " + collected_gold + " gold","gold",Color.WHITE);
			                removed_gold = removed_gold + collected_gold;
			            }
			        	doing_something = true;
			        	continue;
		        	}
		        } else if (waiting_for_chest_noise){
		        	if (chest_noise_at <= clock){
		        		waiting_for_chest_noise = false;
		        		waiting_for_chest_contents = true;
		        		Gamestate.add_sound(chest_noise);
		        		if (chest_successful){
		        			Gamestate.add_message("You manage to get it open.",null,Color.WHITE);
		        		} else {
		        			Gamestate.add_message("But it is locked...",null,Color.WHITE);
		        		}
		        	}
		        	doing_something = true;
		        	continue;
		        } else if (waiting_for_chest_contents){
		        	if (chest_action_at <= clock){
		        		waiting_for_chest_noise = false;
		        		waiting_for_chest_contents = false;
		        		waiting_for_some_reason = true;
		        		if (chest_successful){
			        		if (chest_gold_amount>0)
			        			find_gold(chest_gold_amount);
			        		if (chest_gems_amount>0)
			        			find_gems(chest_gems_amount);
		        		}
		        	}
		        	doing_something = true;
		        	continue;
		        } else if (waiting_for_flying){
		        	if (flying_until < clock){
		                for (Character c : Gamestate.p.chars)
		                    if (c.alive && !c.about_to_die && c.current_stance==Stance_code.ST_FLY)
		                        c.current_stance = null;
		                Gamestate.add_message("You alight...",image,Color.WHITE);
		        		Gamestate.add_sound("fly");
		                waiting_for_flying = false;
		                waiting_for_some_reason = true;
		                continue_at = clock + 1.5;
		        	}
		        	doing_something = true;
		        	continue;
		        } else if (waiting_for_some_reason){
		        	if (continue_at <= clock){
		        		waiting_for_some_reason = false;
		        	}
		        	doing_something = true;
		        	continue;
		        } else if (Gamestate.quit_after_pause || accelerate_fleeing){
		        	doing_something = true;
		        	continue;
		        } else {
		            // do one or more events
		            if (this_event.code==Dungeon_event_code.DE_MOBS){
		                mobs_arrived = clock;
		                //while e.n_surviving_mobs()>(6 - this_event[2]):
		                //    e.weakest_target().flee()
		                for (int i=0; i<this_event.n_of; i++){
		                    Mob m = Gamestate.e.add_mob(this_event.mob_type);
		                    if (m!=null) {
		                        clear = false;
		                    } else {
		                        mob_queue.add(this_event.mob_type);
		                    }
		//                   if m!=null:
		//                       if this_event[2]>1:
		//                           Gamestate.add_message(("Arrived (x" + str(this_event[2]) + ")",m.image,Color.WHITE));
		//                       else:
		//                           Gamestate.add_message(("Arrived",m.image,Color.WHITE));
		                }
		                // are we stealthy?
                        boolean tried = false, worked = true;
                        for (Character c : Gamestate.p.chars){
		                    if (c.alive && !c.about_to_die && c.current_stance==Stance_code.ST_STEALTH){
		                    	tried = true;
		                        for (Character_action a : c.actions){
		                            if (a.action_code==Character_action_code.CA_STEALTH)
		                                if (randint(1,100) > a.chance() || !can_sneak)
		                                    worked = false;
		                        }
		                    }
                        }
                        if (tried){
	                        if (worked){
	                            mobs_will_leave = true;
	                        } else {
	                            Gamestate.add_message("You are not sneaky enough!",null,Color.WHITE);
								for (Character c2 : Gamestate.p.chars){
									if (c2.current_stance==Stance_code.ST_STEALTH)
										c2.current_stance = null;
								}
								mobs_will_leave = false;
	                        }
		                } else {
							mobs_will_leave = false;
						}
		                if (mobs_will_leave){
							for (int i=0; i<Gamestate.e.mobs.size(); i++){
								Mob m = Gamestate.e.mobs.get(i);
		                        if (m.alive && !m.about_to_die && m.perceptive){
		                            Gamestate.add_message("You are discovered!",null,Color.WHITE);
									for (Character c : Gamestate.p.chars){
										if (c.alive && !c.about_to_die && c.current_stance==Stance_code.ST_STEALTH){
											c.current_stance=null;
										}
									}
									mobs_will_leave = false;
		                            continue;
		                        }
		                    }
		                }
		            } else if (this_event.code==Dungeon_event_code.DE_TUTORIAL){
		                tutorial_mode = true;
		                Gamestate.p.xp_multiplier = 0;
		            } else if (this_event.code==Dungeon_event_code.DE_NOPRAY){
		                can_pray = false;
		            } else if (this_event.code==Dungeon_event_code.DE_NOBRIBE){
		                can_bribe = false;
		            } else if (this_event.code==Dungeon_event_code.DE_NOSNEAK){
		                can_sneak = false;
		            } else if (this_event.code==Dungeon_event_code.DE_CLIMB){
		                Gamestate.add_message("The journey is exhausting...",null,Color.WHITE);
		        		Gamestate.add_sound("panting");
		            	for (Character c : Gamestate.p.chars){
		            		if (c.alive && !c.about_to_die){
		            			int cost = 36 / (2 + c.constitution);
		            			if (c.energy > cost){
		            				c.energy = c.energy - cost;
		            			} else {
		    		                Gamestate.add_message("Cannot go another step...",c.image,Color.WHITE);
		    		                c.flee();
		            			}
		            		}
		            	}
		            } else if (this_event.code==Dungeon_event_code.DE_CRAG){
		                can_fall = true;
		            } else if (this_event.code==Dungeon_event_code.DE_ROCK){
		            	boulder_pos = Gamestate.p.random_target(null).pos;
		            	if (boulder_pos!=null)
		            		boulder_dropped = Gamestate.w.clock + 1.0;
		            } else if (this_event.code==Dungeon_event_code.DE_MESSAGE){
		                Gamestate.add_message(this_event.text,this_event.image,Color.WHITE);
		            } else if (this_event.code==Dungeon_event_code.DE_SOUND){
		                Gamestate.add_sound(this_event.sound);
		            } else if (this_event.code==Dungeon_event_code.DE_DOOR){
		                Gamestate.add_message("You open a door...","door",Color.WHITE);
		                Gamestate.add_sound("door");
		            } else if (this_event.code==Dungeon_event_code.DE_LOCKEDDOOR){
		            	if (waiting_gold>0)
		            		find_gold(waiting_gold);
			            waiting_gold = 0;
		                Gamestate.add_message("You come to a locked door...","door",Color.WHITE);
		            	door_pick = false;
		            	door_bash = false;
		            	door_knock = false;
	                    for (Character c : Gamestate.p.chars){
	                        if (c.alive && !c.about_to_die){
	                            for (Character_action a : c.actions){
	                                if (a.action_code==Character_action_code.CA_PICK_LOCKS){
	                                    if (randint(1,100) <= a.chance()){
	                                    	door_pick = true;
	                                    }
	                                } else if (a.action_code==Character_action_code.CA_KNOCK){
                                    	door_knock = true;
	                                } else if (a.action_code==Character_action_code.CA_BASH_DOOR){
                                    	door_bash = true;
	                                }
	                            }
	                        }
	                    }
		                waiting_for_door = true;
		                door_action_at = clock + 1.5;
		            } else if (this_event.code==Dungeon_event_code.DE_UPSTAIRS){
		                Gamestate.add_message("You climb the stairs...","upstairs",Color.WHITE);
		                Gamestate.add_sound("stairs");
		            } else if (this_event.code==Dungeon_event_code.DE_DOWNSTAIRS){
		                Gamestate.add_message("You descend...","stairs",Color.WHITE);
		                Gamestate.add_sound("stairs");
		              /*  for (Character c : Gamestate.p.chars){
		                    if (c.alive && !c.about_to_die && c.current_stance==Stance_code.ST_STEALTH){
		                    	c.current_stance = null;
		                    }
		                } maybe not? */
		            } else if (this_event.code==Dungeon_event_code.DE_NOFLEE){
		            	Gamestate.add_sound("gong");
		            	Gamestate.add_message("You can no longer flee. Win or perish!",null,Color.WHITE);
		                can_flee = false;
		            } else if (this_event.code==Dungeon_event_code.DE_CLEARMOBS){
		                if (Gamestate.e.n_surviving_mobs()>0){
		                    Gamestate.add_message("All the monsters flee!",null,Color.WHITE);
		                    Gamestate.e.flush();
		                }
		            } else if (this_event.code==Dungeon_event_code.DE_GOLD){
		                find_gold(randint(this_event.min,this_event.max));
		            } else if (this_event.code==Dungeon_event_code.DE_GEMS){
		                find_gems(randint(this_event.min,this_event.max));
		            } else if (this_event.code==Dungeon_event_code.DE_WIN){
		                Gamestate.add_message("You have beaten this dungeon!","crown",Color.WHITE);
		                Gamestate.add_sound("fanfare");
		                dungeon_won = true;
		            } else if (this_event.code==Dungeon_event_code.DE_QUIT){
		                int xp = 0;
		                for (Character c : Gamestate.p.chars){
		                	if (tutorial_mode)
		                		c.xp = 350;
		                    if (c.alive && !c.about_to_die)
		                        xp = xp + c.xp;
		                }
		                Gamestate.quit_after_pause = true;
		                Gamestate.add_message("Escaped with " + xp + " XP and " + collected_gold + " gold","gold",Color.WHITE);
		                removed_gold = removed_gold + collected_gold;
		            } else if (this_event.code==Dungeon_event_code.DE_CHECKTRAPS){
		                traps_here = true;
		            } else if (this_event.code==Dungeon_event_code.DE_FEAR){
		                while (true){
		                	Character char_target = Gamestate.p.random_target(null);
		                    if (char_target.alive && !char_target.about_to_die){
		                        boolean immune = false;
		                        for (Character_action a : char_target.actions){
		                            if (a.action_code==Character_action_code.CA_RESIST_FEAR){
		                                immune = true;
		                                continue;
		                            }
		                        }
		                        if (char_target.has_status_effect(Status_effect_code.EF_MEDITATION))
		                            immune = true;
		                        if (!immune){
		                        	char_target.status_effects.put(Status_effect_code.EF_FEAR,new Effect_parameters(randint(4,7)));
		                        	char_target.action_in_progress = null;
		                        	char_target.current_stance = null;
		                        }
		                    }
		                    if (!Gamestate.w.one_in(2))
		                        break;
		                }
                    } else if (this_event.code==Dungeon_event_code.DE_ILLUSIONS){
                        int summoned = 0;
                        int to_summon = 3;
                        for (int i=0; i<to_summon; i++){
                            List<Monster_code> options = Arrays.asList(Monster_code.MOB_BLAST_SPORE,Monster_code.MOB_COBRA,Monster_code.MOB_GIANT_FIREFLY,Monster_code.MOB_GIANT_WASP,Monster_code.MOB_JELLY,Monster_code.MOB_MILLIPEDE,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_PYTHON,Monster_code.MOB_SKELETON,Monster_code.MOB_TROGLODYTE_ARCHER,Monster_code.MOB_WOLF,Monster_code.MOB_LEPRECHAUN,Monster_code.MOB_OOZE,Monster_code.MOB_ORC_AGITATOR,Monster_code.MOB_ORC_SLAVEMASTER,Monster_code.MOB_ORC_WITH_MANCATCHER,Monster_code.MOB_SPECTRE,Monster_code.MOB_TROGLODYTE_SHAMAN,Monster_code.MOB_CLAY_GOLEM,Monster_code.MOB_NAGA_ARCHER,Monster_code.MOB_NAGA_WARRIOR,Monster_code.MOB_OGRE,Monster_code.MOB_ORC_CHAMPION,Monster_code.MOB_TROGLODYTE_PRIEST,Monster_code.MOB_TURTLOID,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_WIGHT,Monster_code.MOB_WRAITH,Monster_code.MOB_ARTIFICER,Monster_code.MOB_BASILISK,Monster_code.MOB_BLACK_KNIGHT,Monster_code.MOB_BLACK_OGRE,Monster_code.MOB_BURNING_GHOST,Monster_code.MOB_DARK_DWARF,Monster_code.MOB_DARK_ELF_ARCHER,Monster_code.MOB_DARK_PRIEST,Monster_code.MOB_LIME_JELLY,Monster_code.MOB_MAGE,Monster_code.MOB_MAGEBANE_EYE,Monster_code.MOB_MUTE_GHOST,Monster_code.MOB_SHADOW,Monster_code.MOB_RAVEN,Monster_code.MOB_HALBERDIER,Monster_code.MOB_HALF_TROLL,Monster_code.MOB_BABY_RED_DRAGON,Monster_code.MOB_BABY_GREEN_DRAGON,Monster_code.MOB_BABY_BLUE_DRAGON,Monster_code.MOB_WHIRLWIND,Monster_code.MOB_IMP,Monster_code.MOB_QUASIT,Monster_code.MOB_GUARDIAN_EYE,Monster_code.MOB_ILLUSIONIST,Monster_code.MOB_TIME_VORTEX,Monster_code.MOB_LIGHTNING_DOG,Monster_code.MOB_BIG_SNAKE,Monster_code.MOB_VORTEX,Monster_code.MOB_CLAWED_SPECTRE,Monster_code.MOB_WINGED_SPECTRE,Monster_code.MOB_EYE_OF_NEGATION,Monster_code.MOB_DRIDER,Monster_code.MOB_LIZARDMAN);
                            Mob new_critter = Gamestate.e.add_mob(options.get(randint(0,options.size()-1)));
                            if (new_critter!=null && new_critter.level<=this_event.max_level){
                                new_critter.from_summoning_trap = false; // nope can't dispel it
                                new_critter.slide_from = null;
                                new_critter.fade_in = true;
                                new_critter.fade_start = clock;
                                new_critter.fade_finish = clock + 0.5;
                                new_critter.illusion = true;
                                summoned = summoned + 1;
                                clear = false;
						    }
                        }
                    } else if (this_event.code==Dungeon_event_code.DE_WEBTRAP){
		                for (Character c : Gamestate.p.chars){
		                    if (c.alive && !c.about_to_die){
		                        boolean immune = false;
		                        for (Character_action a : c.actions){
		                            if (a.action_code==Character_action_code.CA_FREE_ACTION){
		                                immune = true;
		                                continue;
		                            }
		                        }
	                	        double d = Gamestate.w.randint(5, 12);
	                	        if (c.strength>4) d = d/2;
	                	        if (c.cunning>3) d = d/3;
	                        	c.status_effects.put(Status_effect_code.EF_PARALYSIS,new Effect_parameters(d));
	                        	c.webbed_until = Gamestate.w.clock + d;
	                        	c.action_in_progress = null;
	                        	c.current_stance = null;
		                    }
		                }
		            } else if (this_event.code==Dungeon_event_code.DE_PIT || this_event.code==Dungeon_event_code.DE_EXPLOSIVERUNE || this_event.code==Dungeon_event_code.DE_GASTRAP || this_event.code==Dungeon_event_code.DE_SUMMONINGRUNE || this_event.code==Dungeon_event_code.DE_SPIKEDPIT || this_event.code==Dungeon_event_code.DE_TELEPORTRUNE || this_event.code==Dungeon_event_code.DE_FIRETRAP || this_event.code==Dungeon_event_code.DE_ILLUSIONSRUNE){
		                traps_here = false;
		                boolean escape = false;
		                for (Character c : Gamestate.p.chars){
		                    if (c.alive && !c.about_to_die){
		                        if (c.has_status_effect(Status_effect_code.EF_REVEAL_DANGER)){
									c.status_effects.get(Status_effect_code.EF_REVEAL_DANGER).lasts_until = 0;
		                            escape = true;
		                        }
		                    }
		                }
		                if (escape){
		            		Gamestate.add_sound("trap");
		                    Gamestate.add_message("You evade a trap!","pit",Color.WHITE);
		                } else {
		                    if (this_event.code==Dungeon_event_code.DE_PIT){
		                        Character a = Gamestate.p.random_front_target(null);
		                        Gamestate.add_message(a.name + " has fallen into a pit!","pit",Color.WHITE);
				    			Gamestate.add_sound("pit");
		                        a.take_damage(randint(5,15));
		                    } else if (this_event.code==Dungeon_event_code.DE_TELEPORTRUNE){
		                        Character a = Gamestate.p.get_char_at_pos(Position_code.POS_C_FRONT_2);
		                        if (a!=null && a.alive && !a.about_to_die){
			                        Gamestate.add_message("You have set off an teleport rune!","teleport_rune",Color.WHITE);
			                        Gamestate.add_sound("bad_spell");
			    	                a.pos = null;
			    	                a.alive = false;
			    	                a.fled = true;
			    	                Gamestate.p.after_character_leaves(false);
		    	                } else {
			                        Gamestate.add_message("You spotted a teleport rune!","teleport_rune",Color.WHITE);
			                        Gamestate.add_message("If a character had been in the middle of the front row, they would have stepped on it.",null,Color.WHITE);
		                        }
		                    } else if (this_event.code==Dungeon_event_code.DE_EXPLOSIVERUNE){
		                        Gamestate.add_message("You have set off an explosive rune!","explosive_rune",Color.WHITE);
		                        Gamestate.add_sound("bang");
		                        boolean in_cover = false;
		                        for (Character c : Gamestate.p.chars){
		                        	if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_TAKE_COVER){
		                        		in_cover = true;
		                        		c.current_stance = null;
		                        	}
		                        }
		                        if (!in_cover){
			                        for (Position_code pos : Arrays.asList(Position_code.POS_C_FRONT_1,Position_code.POS_C_FRONT_2,Position_code.POS_C_FRONT_3)){
			                            Character a = Gamestate.p.get_char_at_pos(pos);
			                            if (a!=null)
			                                a.take_damage(randint(3,7));
			                    	}
		                        } else {
			                        Gamestate.add_message("But you take cover from the blast...",null,Color.WHITE);
		                        }
		                    } else if (this_event.code==Dungeon_event_code.DE_FIRETRAP){
		                        Gamestate.add_message("You have set off a fireball trap!","fire_trap",Color.WHITE);
		                        Gamestate.add_sound("breath");
		                        boolean in_cover = false;
		                        for (Character c : Gamestate.p.chars){
		                        	if (c.alive && !c.about_to_die && c.current_stance!=null && c.current_stance==Stance_code.ST_TAKE_COVER){
		                        		in_cover = true;
		                        		c.current_stance = null;
		                        	}
		                        }
		                        if (!in_cover){
			                        for (Character a : Gamestate.p.chars){
			                            if (a!=null && a.alive && !a.about_to_die){
			                            	int damage = randint(20,40);
 			                         	    if (a.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE))
			                                   damage = damage / 4;
			                                else if (a.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || a.fire_resistance)
			                                   damage = damage / 2;
			                                a.take_damage(damage);
			                                a.flash_image = "fire_trap";
			                                a.flash_long = true;
			                            }
			                    	}
		                        } else {
			                        Gamestate.add_message("But you take cover from the blast...",null,Color.WHITE);
		                        }
		                    } else if (this_event.code==Dungeon_event_code.DE_GASTRAP){
		                        Gamestate.add_message("You have set off a gas trap!","gas_trap",Color.WHITE);
		                        Gamestate.add_sound("gas");
		                        poison_cloud_until = clock + 8.0;
		                        poison_cloud_from = null;
		                    } else if (this_event.code==Dungeon_event_code.DE_SUMMONINGRUNE){
		                        Gamestate.add_message("Suddenly monsters appear in front of you!","summoning_rune",Color.WHITE);
		                        Gamestate.add_sound("summon");
		                        int summoned = 0;
		                        int to_summon = 3;
		                        for (int i=0; i<to_summon; i++){
		                            List<Monster_code> options = Arrays.asList(Monster_code.MOB_BLAST_SPORE,Monster_code.MOB_COBRA,Monster_code.MOB_GIANT_FIREFLY,Monster_code.MOB_GIANT_WASP,Monster_code.MOB_JELLY,Monster_code.MOB_MILLIPEDE,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_PYTHON,Monster_code.MOB_SKELETON,Monster_code.MOB_TROGLODYTE_ARCHER,Monster_code.MOB_WOLF,Monster_code.MOB_LEPRECHAUN,Monster_code.MOB_OOZE,Monster_code.MOB_ORC_AGITATOR,Monster_code.MOB_ORC_SLAVEMASTER,Monster_code.MOB_ORC_WITH_MANCATCHER,Monster_code.MOB_SPECTRE,Monster_code.MOB_TROGLODYTE_SHAMAN,Monster_code.MOB_CLAY_GOLEM,Monster_code.MOB_NAGA_ARCHER,Monster_code.MOB_NAGA_WARRIOR,Monster_code.MOB_OGRE,Monster_code.MOB_ORC_CHAMPION,Monster_code.MOB_TROGLODYTE_PRIEST,Monster_code.MOB_TURTLOID,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_WIGHT,Monster_code.MOB_WRAITH,Monster_code.MOB_ARTIFICER,Monster_code.MOB_BASILISK,Monster_code.MOB_BLACK_KNIGHT,Monster_code.MOB_BLACK_OGRE,Monster_code.MOB_BURNING_GHOST,Monster_code.MOB_DARK_DWARF,Monster_code.MOB_DARK_ELF_ARCHER,Monster_code.MOB_DARK_PRIEST,Monster_code.MOB_LIME_JELLY,Monster_code.MOB_MAGE,Monster_code.MOB_MAGEBANE_EYE,Monster_code.MOB_MUTE_GHOST,Monster_code.MOB_SHADOW,Monster_code.MOB_RAVEN,Monster_code.MOB_HALBERDIER,Monster_code.MOB_HALF_TROLL,Monster_code.MOB_BABY_RED_DRAGON,Monster_code.MOB_BABY_GREEN_DRAGON,Monster_code.MOB_BABY_BLUE_DRAGON,Monster_code.MOB_WHIRLWIND,Monster_code.MOB_IMP,Monster_code.MOB_QUASIT,Monster_code.MOB_GUARDIAN_EYE,Monster_code.MOB_ILLUSIONIST,Monster_code.MOB_TIME_VORTEX,Monster_code.MOB_LIGHTNING_DOG,Monster_code.MOB_BIG_SNAKE,Monster_code.MOB_VORTEX,Monster_code.MOB_CLAWED_SPECTRE,Monster_code.MOB_WINGED_SPECTRE,Monster_code.MOB_EYE_OF_NEGATION,Monster_code.MOB_DRIDER,Monster_code.MOB_LIZARDMAN,Monster_code.MOB_GIANT_SKELETON);
		                            Mob new_critter = Gamestate.e.add_mob(options.get(randint(0,options.size()-1)));
		                            if (new_critter!=null && new_critter.level<=this_event.max_level){
		                                new_critter.from_summoning_trap = true;
		                                new_critter.slide_from = null;
		                                new_critter.fade_in = true;
		                                new_critter.fade_start = clock;
		                                new_critter.fade_finish = clock + 0.5;
		                                summoned = summoned + 1;
		                                clear = false;
								    }
		                        }
		                    } else if (this_event.code==Dungeon_event_code.DE_ILLUSIONSRUNE){
		                        Gamestate.add_message("Suddenly enemies appear in front of you!","summoning_rune",Color.WHITE);
		                        Gamestate.add_sound("summon");
		                        int summoned = 0;
		                        int to_summon = 3;
		                        for (int i=0; i<to_summon; i++){
		                            List<Monster_code> options = Arrays.asList(Monster_code.MOB_BLAST_SPORE,Monster_code.MOB_COBRA,Monster_code.MOB_GIANT_FIREFLY,Monster_code.MOB_GIANT_WASP,Monster_code.MOB_JELLY,Monster_code.MOB_MILLIPEDE,Monster_code.MOB_ORC_WARRIOR,Monster_code.MOB_PYTHON,Monster_code.MOB_SKELETON,Monster_code.MOB_TROGLODYTE_ARCHER,Monster_code.MOB_WOLF,Monster_code.MOB_LEPRECHAUN,Monster_code.MOB_OOZE,Monster_code.MOB_ORC_AGITATOR,Monster_code.MOB_ORC_SLAVEMASTER,Monster_code.MOB_ORC_WITH_MANCATCHER,Monster_code.MOB_SPECTRE,Monster_code.MOB_TROGLODYTE_SHAMAN,Monster_code.MOB_CLAY_GOLEM,Monster_code.MOB_NAGA_ARCHER,Monster_code.MOB_NAGA_WARRIOR,Monster_code.MOB_OGRE,Monster_code.MOB_ORC_CHAMPION,Monster_code.MOB_TROGLODYTE_PRIEST,Monster_code.MOB_TURTLOID,Monster_code.MOB_WHITE_WOLF,Monster_code.MOB_WIGHT,Monster_code.MOB_WRAITH,Monster_code.MOB_ARTIFICER,Monster_code.MOB_BASILISK,Monster_code.MOB_BLACK_KNIGHT,Monster_code.MOB_BLACK_OGRE,Monster_code.MOB_BURNING_GHOST,Monster_code.MOB_DARK_DWARF,Monster_code.MOB_DARK_ELF_ARCHER,Monster_code.MOB_DARK_PRIEST,Monster_code.MOB_LIME_JELLY,Monster_code.MOB_MAGE,Monster_code.MOB_MAGEBANE_EYE,Monster_code.MOB_MUTE_GHOST,Monster_code.MOB_SHADOW,Monster_code.MOB_RAVEN,Monster_code.MOB_HALBERDIER,Monster_code.MOB_HALF_TROLL,Monster_code.MOB_BABY_RED_DRAGON,Monster_code.MOB_BABY_GREEN_DRAGON,Monster_code.MOB_BABY_BLUE_DRAGON,Monster_code.MOB_WHIRLWIND,Monster_code.MOB_IMP,Monster_code.MOB_QUASIT,Monster_code.MOB_GUARDIAN_EYE,Monster_code.MOB_ILLUSIONIST,Monster_code.MOB_TIME_VORTEX,Monster_code.MOB_LIGHTNING_DOG,Monster_code.MOB_BIG_SNAKE,Monster_code.MOB_VORTEX,Monster_code.MOB_CLAWED_SPECTRE,Monster_code.MOB_WINGED_SPECTRE,Monster_code.MOB_EYE_OF_NEGATION,Monster_code.MOB_DRIDER,Monster_code.MOB_LIZARDMAN);
		                            Mob new_critter = Gamestate.e.add_mob(options.get(randint(0,options.size()-1)));
		                            if (new_critter!=null && new_critter.level<=this_event.max_level){
		                                new_critter.from_summoning_trap = false; // nope can't dispel it
		                                new_critter.slide_from = null;
		                                new_critter.fade_in = true;
		                                new_critter.fade_start = clock;
		                                new_critter.fade_finish = clock + 0.5;
		                                new_critter.illusion = true;
		                                summoned = summoned + 1;
		                                clear = false;
								    }
		                        }
		                    } else if (this_event.code==Dungeon_event_code.DE_SPIKEDPIT){
		                        Character a = Gamestate.p.random_front_target(null);
				    			Gamestate.add_sound("spiked_pit");
		                        Gamestate.add_message(a.name + " has fallen into a spiked pit!","spiked_pit",Color.WHITE);
		                        a.take_damage(randint(10,30));
		                    }
		                }
		            } else if (this_event.code==Dungeon_event_code.DE_LOOT){
		            	if (waiting_gold>0)
		            		find_gold(waiting_gold);
			            waiting_gold = 0;
						double increase = 1.0;
						for (Character c : Gamestate.p.chars){
							if (c.alive && !c.about_to_die){
								for (Character_action a : c.actions){
									if (a.action_code==Character_action_code.CA_DETECT_MAGIC){
										increase = increase + (a.increase()-1);
									}
								}
							}
						}
		    			if (Gamestate.w.rand.nextDouble() < this_event.loot_chance * increase){
							items_found.add(this_event.loot_type);
							Item item = Campaignstate.i.make_item(this_event.loot_type);
							String item_image = this_event.loot_type.get_image();
							if (item_image==null || item_image.equals(""))
								item_image = "loot";
							Gamestate.add_message("Found: "+ item.name(),item_image,Color.WHITE);
							Gamestate.add_sound("item");
						}
		            } else if (this_event.code==Dungeon_event_code.DE_GOLDCHEST){
		            	if (waiting_gold>0)
		            		find_gold(waiting_gold);
			            waiting_gold = 0;
			            Gamestate.add_message("You have found a chest!","gold_chest",Color.WHITE);
		                boolean got_it = true;
		                if (randint(1,100)<=this_event.locked_chance){
		                    got_it = false;
		                    for (Character c : Gamestate.p.chars){
		                        if (!got_it && c.alive && !c.about_to_die){
		                            for (Character_action a : c.actions){
		                                if (a.action_code==Character_action_code.CA_PICK_LOCKS){
		                                    if (randint(1,100) <= a.chance()){
		                                        got_it = true;
		                                    }
		                                }
		                            }
		                        }
		                    }
		                }
		                waiting_for_chest_noise = true;
		                if (got_it){
		                	chest_successful = true;
		                	chest_noise = "unlocking";
		                	chest_noise_at = clock + 1.5;
		                	chest_action_at = clock + 2.5;
		                	continue_at = clock + 3.5;
		                	chest_gold_amount = randint(this_event.min,this_event.max);
		                	chest_gems_amount = 0;
		                } else {
		                	chest_successful = false;
		                	chest_noise = "cancel";
		                	chest_noise_at = clock + 1.5;
		                	chest_action_at = clock + 2.5;
		                	continue_at = clock + 3.5;
		                }
		            } else if (this_event.code==Dungeon_event_code.DE_GEMSCHEST){
			            find_gold(waiting_gold);
			            waiting_gold = 0;
		                Gamestate.add_message("You have found a chest!","gems_chest",Color.WHITE);
		                boolean got_it = true;
		                if (randint(1,100)<=this_event.locked_chance){
		                    got_it = false;
		                    for (Character c : Gamestate.p.chars){
		                        if (!got_it && c.alive && !c.about_to_die){
		                            for (Character_action a : c.actions){
		                                if (a.action_code==Character_action_code.CA_PICK_LOCKS){
		                                    if (randint(1,100) <= a.chance()){
		                                        got_it = true;
		                                    }
		                                }
		                            }
		                        }
		                    }
		                }
		                waiting_for_chest_noise = true;
		                if (got_it){
		                	chest_successful = true;
		                	chest_noise = "unlocking";
		                	chest_noise_at = clock + 1.5;
		                	chest_action_at = clock + 2.5;
		                	chest_gems_amount = randint(this_event.min,this_event.max);
		                	chest_gold_amount = 0;
		                } else {
		                	chest_successful = false;
		                	chest_noise = "cancel";
		                	chest_noise_at = clock + 1.5;
		                	chest_action_at = clock + 2.5;
		                }
		            } else if (this_event.code==Dungeon_event_code.DE_NOFLY){
		                no_fly = true;
		            } else if (this_event.code==Dungeon_event_code.DE_CANFLY){
		                no_fly = false;
		            }
		            started_pause_at = -1;
		            started_respite_at = -1;
		            event_no = event_no + 1;
		        }
		    }
		    // has player decided to flee?
		    if (party_fleeing && (start_fleeing_at<0)){
		        start_fleeing_at = clock;
		    }
		    // is any character ready to flee?
		    double due_to_flee;
		    if (accelerate_fleeing){
		    	due_to_flee = start_fleeing_at + 0.2 * (n_fled+1);
		    } else {
		        due_to_flee = start_fleeing_at + 2 * (n_fled+1);
		    }
		    if (party_fleeing && clock > due_to_flee && Gamestate.p.n_surviving_chars()>0){
		        Character t = Gamestate.p.random_back_target(null);
		        if (t!=null){
		            n_fled = n_fled + t.flee();
		        } else {
		            t = Gamestate.p.random_front_target(null);
		            n_fled = n_fled + t.flee();
		        }
		    }
		    if (mobs_will_leave){
		        // party are stealthy, mobs all buzz off
		        if (mobs_arrived < clock - 1){
		            Gamestate.add_message("You sneak past the monsters!",null,Color.WHITE);
		            mobs_will_leave = false;
		            while (Gamestate.e.n_surviving_mobs()>0){
		            	Mob leaving = Gamestate.e.weakest_target();
		            	leaving.slide_from = leaving.pos;
		            	leaving.slide_to = Position_code.POS_BACK_RIGHT;
		            	leaving.remove();
		            	leaving.slide_start = Gamestate.w.clock;
		            	leaving.slide_finish = Gamestate.w.clock + 0.3;
		            }
		        }
		    } else {
		        for (Character c : Gamestate.p.chars){
		            if (c.can_act() && Gamestate.e.n_surviving_front_mobs()>0 && (c.current_stance==null || c.current_stance==Stance_code.ST_BERSERK)){
		                // front row characters get default melee attack
		                if (c.front_row()){
		                    if (c.melee_attack){
		                        if (one_in((int)Math.floor(c.current_melee_period() / elapsed_since_last_process))){
		                            Mob target = Gamestate.e.random_front_target(c);
		                            if (target!=null){
		                                if (c.melee_hit_roll(target)){
		                                    Gamestate.add_sound(c.melee_hit_sound);
		                                    int damage = c.melee_damage_roll(target);
		                                    int armor = target.current_armor();
		                                    if (c.has_status_effect(Status_effect_code.EF_FIND_WEAKNESS))
		                                        armor = Math.max(0,armor-5);
		                                    if (damage > armor){
		                                        c.flash_image = "pale";
		                                        target.take_damage(damage-armor);
		                                        if (c.energy_for_hit)
		                                            c.energy = Math.min(c.max_energy,Math.min(c.energy + 2,c.energy+target.level/2));
		                                        if (c.has_status_effect(Status_effect_code.EF_LIFE_LEECH)){
		                                            int old_health = c.health;
		                                            c.health = Math.min(c.health+(damage-armor)/2,c.max_health);
		                                            if (c.health > old_health){
		                                            	c.flash_number = c.health - old_health;
		                                            	c.number_starts = Gamestate.w.clock;
		                                            	c.number_color = Color.GREEN;
		                                            }
		                                        }
		                                    }
		                                } else {
		                                    Gamestate.add_sound(c.melee_miss_sound);
		                                }
		                            }
		                        }
		                    }
		                // back row characters get default missile attack
		                } else if (c.missile_attack){
		                    if (one_in((int)Math.floor(c.current_missile_period() / elapsed_since_last_process))){
		                        Mob target = Gamestate.e.random_front_target(c);
		                        if (target!=null){
		                            if (c.missile_hit_roll(target)){
		                                Gamestate.add_sound(c.missile_hit_sound);
		                                int damage = c.missile_damage_roll(target);
		                                if (damage > target.current_armor()){
		                                    c.flash_image = "pale";
		                                    target.take_damage(damage-target.current_armor());
		                                    if (c.energy_for_shot)
		                                    	c.energy = Math.min(c.max_energy,Math.min(c.energy + 2,c.energy+target.level/2));
		                                    if (!target.front_row())
		                                        target.has_been_shot = true;
		                                }
		                            } else {
		                                Gamestate.add_sound(c.missile_miss_sound);
		                            }
		                        }
		                    }
		                }
		            }
		        }
		        if (Gamestate.p.n_surviving_chars()>0){
			        for (int i=0; i<Gamestate.e.mobs.size(); i++){
						Mob m = Gamestate.e.mobs.get(i);
			            // is it on fire || poisoned?
			            if (m.alive && !m.about_to_die){
			                if (m.on_fire_until>0 && m.on_fire_until > clock && !m.fire_resistant){
			                    if (one_in((int)Math.floor(m.fire_period / elapsed_since_last_process)))
			                        m.take_damage(1);
			                }
			            }
				        if (m.alive && !m.about_to_die){
			                if (m.poisoned_until>0 && m.poisoned_until > clock && !m.poison_resistant){
			                    if (one_in((int)Math.floor(m.poison_period / elapsed_since_last_process)))
			                        m.take_damage(1);
			                }
			            }
				        if (m.alive && !m.about_to_die){
			                if (m.regenerate && m.health < m.max_health){
			                    if (m.max_health >= 225){
			                    	// war troll
			                        if (one_in((int)Math.floor(0.1 / elapsed_since_last_process)))
			                            m.health = m.health + 1;
			                    } else if (m.max_health >= 100){
			                    	// berserker, troll
			                        if (one_in((int)Math.floor(0.15 / elapsed_since_last_process)))
			                            m.health = m.health + 1;
			                    } else {
			                    	// ooze, half troll
			                        if (one_in((int)Math.floor(0.3 / elapsed_since_last_process)))
			                            m.health = m.health + 1;
			                    }
			                }
			                if (m.split && m.health > 0.25*m.max_health && Gamestate.e.n_surviving_mobs() < 6){
			                	double period = 5.0;
			                	if (Gamestate.e.n_surviving_mobs()==2)
			                		period = 3.75;
			                	else if (Gamestate.e.n_surviving_mobs()==1)
			                		period = 2.5;
			                	if (m.slow_until > clock) period=period*2;
			                    if (one_in((int)Math.floor(period / elapsed_since_last_process))){
			                		Gamestate.add_sound("splut");
			                        Mob new_mob = Gamestate.e.add_mob(m.code);
			                        if (new_mob!=null){
			                            new_mob.xp_multiplier = m.xp_multiplier / 2;
			                            new_mob.slide_from = m.pos;
			                            Gamestate.add_message("Splits!",m.image,Color.WHITE);
			                            new_mob.health = Math.max(1,(2 * m.health) / 3);
			                            m.health = Math.max(1,(2 * m.health) / 3);
			                            m.max_health = Math.max(m.health, m.max_health - 10);
			                            new_mob.max_health = Math.max(new_mob.health, new_mob.max_health - 10);
			                        }
			                    }
			                }
			            }
			            // is it rallied?
			            boolean rallied = (m.rallied_until > clock);
			            // recover from confusion && stunning
			            if (m.alive && !m.about_to_die && m.confused && m.confused_until < clock){
			                m.confused = false;
			                Gamestate.add_message("Recovers",m.image,Color.WHITE);
			            }
			            if (m.alive && !m.about_to_die && m.stunned && m.stunned_until < clock){
			                m.stunned = false;
			                // Gamestate.add_message("Recovers",m.image,Color.WHITE);
			            }
			             // can it act?
			            if (m.can_act()){
			                boolean has_acted = false;
			                // a mob may be committed to moving
			                if (m.move_to_front_at>0 && m.move_to_front_at<clock){
			                    if (Gamestate.e.last_free_front_pos()!=null){
			                    	// massive kludge
			                        if (m.code==Monster_code.MOB_HARPY || m.code==Monster_code.MOB_YOUNG_RED_DRAGON || m.code==Monster_code.MOB_YOUNG_BLUE_DRAGON || m.code==Monster_code.MOB_ROC){
			                    		Gamestate.add_sound("fly");}
			                        m.slide_from = m.pos;
			                        m.slide_start = clock;
			                        m.slide_finish = clock + 0.1;
			                        m.pos = Gamestate.e.last_free_front_pos();
			                        m.slide_to = m.pos;
			                    }
			                    m.move_to_front_at = -1;
			                }
			                if (m.move_to_back_at>0 && m.move_to_back_at<clock){
			                    if (Gamestate.e.last_free_back_pos()!=null){
                                    Gamestate.add_sound("fly");
			                        m.slide_from = m.pos;
			                        m.slide_start = clock;
			                        m.slide_finish = clock + 0.1;
			                        m.pos = Gamestate.e.last_free_back_pos();
			                        m.slide_to = m.pos;
			                    }
			                    m.move_to_back_at = -1;
			                }
			                // if it is at the back, it must move forward if the front row is empty, unless there is a live back mob that may move forward later
			                if (m.alive && !m.about_to_die && !m.front_row() && (m.move_to_front_at <= clock) && Gamestate.e.n_surviving_front_mobs()==0 && Gamestate.e.n_front_mobs_taking_up_space()==0){
			                    boolean ok = false;
			                    // massive kludge
								for (int j=0; j<Gamestate.e.mobs.size(); j++){
									Mob mm = Gamestate.e.mobs.get(j);
			                        if (mm.alive && !m.about_to_die && (m.code==Monster_code.MOB_HARPY || m.code==Monster_code.MOB_HILL_GIANT || m.code==Monster_code.MOB_YOUNG_RED_DRAGON || m.code==Monster_code.MOB_YOUNG_BLUE_DRAGON || m.code==Monster_code.MOB_ROC || m.move_to_front_at >= clock)){
			                            ok = true;
			                            break;
			                        }
			                    }
			                    if (!ok)
			                        m.move_to_front_at = clock + 0.5;
			                }
			                // a confused mob may wander around or even whack another mob
			                if (m.alive && !m.about_to_die && m.confused_until >= clock){
			                    if (m.melee_attack && !m.stunned){
			                        double period = m.melee_period * 2.0;
			                        if (one_in((int)Math.floor(period / elapsed_since_last_process))){
			                            Mob target = null;
			                            if (m.front_row() && Gamestate.e.n_surviving_front_mobs()>1){
			                                target = Gamestate.e.random_front_target(null);
			                                if (target.id==m.id)
			                                    target = null;
			                            }
			                            if (!m.front_row() && Gamestate.e.n_surviving_back_mobs()>1){
			                                target = Gamestate.e.random_back_target(null);
			                                if (target.id==m.id)
			                                    target = null;
			                            }
			                            if (target!=null){
			                                if (randint(1,20) <= 2 * (5 + m.accuracy - target.current_evasion())){
			                                    Gamestate.add_sound(m.melee_hit_sound);
			                                    int damage = m.melee_damage_roll();
			                                    int armor = target.current_armor();
			                                    if (damage > armor){
			                                        Gamestate.add_message("Hits " + target.name,m.image,Color.WHITE);
			                                        target.take_damage(Math.max(0,damage-armor));
			                                        m.flash_image = "pale";
			                                    }
			                                } else {
			                                    Gamestate.add_sound(m.melee_miss_sound);
			                                }
			                            }
			                        }
			                    }
			                    if (one_in((int)Math.floor(4.0 / elapsed_since_last_process))){
			                        if (m.front_row() && Gamestate.e.n_surviving_front_mobs()<3){
			                            Position_code to = Gamestate.e.random_free_front_pos();
			                            if (to!=null){
			                                m.slide_from = m.pos;
			                                m.slide_start = clock;
			                                m.slide_finish = clock + 0.1;
			                                m.pos = to;
			                                m.slide_to = m.pos;
			                            }
			                        } else if (!m.front_row() && Gamestate.e.n_surviving_back_mobs()<3){
			                        	Position_code to = Gamestate.e.random_free_back_pos();
			                            if (to!=null){
			                                m.slide_from = m.pos;
			                                m.slide_start = clock;
			                                m.slide_finish = clock + 0.1;
			                                m.pos = to;
			                                m.slide_to = m.pos;
			                            }
			                        }
			                    }
			                }
			                // does the mob want to act?
		                	// is it partway through a series of flurry attacks?
		                	if (m.n_flurries_remaining>0 && !m.stunned && !m.confused && Gamestate.p.n_surviving_front_chars()>0){
		                		m.n_flurries_remaining = m.n_flurries_remaining-1;
			                    Monster_action ma = new Flurry_smite_MA();
			                    ma.m = m;
	                        	ma.char_target = null;
	                            for (int k=0; k<10; k++){
	                                Character test_target = Gamestate.p.random_target(m);
	                                if (ma.char_valid_targets().contains(test_target))
	                                	ma.char_target = test_target;
	                        	}
	                            if (ma.char_target==null){
	                            	List<Character> valid_targets = ma.char_valid_targets();
	                            	ma.char_target = valid_targets.get(randint(0,valid_targets.size()-1));
	                            }
	                            ma.trigger();
			                    has_acted = true;
			                    break;
		                	} else if (m.n_small_flurries_remaining>0 && !m.stunned && !m.confused && Gamestate.p.n_surviving_front_chars()>0){
		                		m.n_small_flurries_remaining = m.n_small_flurries_remaining-1;
			                    Monster_action ma = new Small_flurry_smite_MA();
			                    ma.m = m;
	                        	ma.char_target = null;
	                            for (int k=0; k<10; k++){
	                                Character test_target = Gamestate.p.random_target(m);
	                                if (ma.char_valid_targets().contains(test_target))
	                                	ma.char_target = test_target;
	                        	}
	                            if (ma.char_target==null){
	                            	List<Character> valid_targets = ma.char_valid_targets();
	                            	ma.char_target = valid_targets.get(randint(0,valid_targets.size()-1));
	                            }
	                            ma.trigger();
			                    has_acted = true;
			                    break;
		                	} else if (m.n_chest_flurries_remaining>0 && !m.stunned && !m.confused && Gamestate.p.n_surviving_front_chars()>0){
		                		m.n_chest_flurries_remaining = m.n_chest_flurries_remaining-1;
			                    Monster_action ma = new Chest_flurry_smite_MA();
			                    ma.m = m;
	                        	ma.char_target = null;
	                            for (int k=0; k<10; k++){
	                                Character test_target = Gamestate.p.random_target(m);
	                                if (ma.char_valid_targets().contains(test_target))
	                                	ma.char_target = test_target;
	                        	}
	                            if (ma.char_target==null){
	                            	List<Character> valid_targets = ma.char_valid_targets();
	                            	ma.char_target = valid_targets.get(randint(0,valid_targets.size()-1));
	                            }
	                            ma.trigger();
			                    has_acted = true;
			                    break;
		                	} else if (!m.blocked_by_gc() && !m.illusion){
			                	// it is not partway through a series of flurry attacks
			                    for (Monster_action a : m.actions){
			                        double ma_period = a.period();
			                        if (a.n_times_used < a.n_uses())
			                        	ma_period = ma_period / a.likelihood_increase_until_n_uses();
			                        if (rallied)
			                        	ma_period = ma_period * 0.7;
			                        if (m.confused)
			                        	ma_period = ma_period * 3.0;
			                        if (m.stunned)
			                        	ma_period = ma_period * 99;
				                	if (m.slow_until > clock) ma_period=ma_period*2;
			                        if (a.ok() && one_in((int)Math.floor(ma_period / elapsed_since_last_process))){
			                            if (a.target_friend()){
			                            	a.mob_target = null;
			                            	List<Mob> valid_targets = a.mob_valid_targets();
			                            	if (valid_targets.size()>0)
			                            		a.mob_target = valid_targets.get(randint(0,valid_targets.size()-1));
			                            	a.trigger();
			                            } else if (a.target_enemy()){
			                            	a.char_target = null;
			                                for (int k=0; k<10; k++){
			                                    Character test_target = Gamestate.p.random_target(m);
			                                    if (a.char_valid_targets().contains(test_target))
			                                    	a.char_target = test_target;
			                            	}
			                                if (a.char_target==null){
			                                	List<Character> valid_targets = a.char_valid_targets();
			                                	a.char_target = valid_targets.get(randint(0,valid_targets.size()-1));
			                                }
					                        a.trigger();
			                            } else {
			                            	a.trigger();
			                            }
			                            has_acted = true;
			                            break;
			                        }
			                    }
		                	}
			                if (has_acted)
			                    continue;
			                // front row mobs get their default melee attack
			                if (m.front_row() && m.melee_attack){
			                    double mme_period = m.melee_period;
			                    if (rallied)
			                    	mme_period = mme_period * 0.7;
			                    if (m.confused)
			                    	mme_period = mme_period * 2.0;
			                    if (m.stunned)
			                    	mme_period = mme_period * 99;
			                	if (m.slow_until > clock) mme_period=mme_period*2;
			                    if (one_in((int)Math.floor(mme_period / elapsed_since_last_process))){
			                        Character mme_target = Gamestate.p.random_front_target(m);
			                        if (mme_target!=null){
			                            if (m.melee_hit_roll(mme_target)){
			                                Gamestate.add_sound(m.melee_hit_sound);
			                                int damage = m.melee_damage_roll();
			                                if (mme_target.physical_resistance)
			                                	damage = (3 * damage)/4;
			                                int armor = mme_target.current_armor();
			                                if (m.AP_melee)
			                                    armor = 0;
			                                if (m.fire_melee && (mme_target.has_status_effect(Status_effect_code.EF_IMMUNE_FIRE)))
			                                    damage = damage / 4;
			                                else if (m.fire_melee && (mme_target.has_status_effect(Status_effect_code.EF_RESIST_FIRE) || mme_target.fire_resistance))
			                                    damage = damage / 2;
			                                if (damage > armor){
			                                    mme_target.take_damage(Math.max(0,damage-armor));
			                                    m.flash_image = "pale";
			                                    // it might be a poison attack;
			                                    if (m.poison_melee>0 && mme_target.alive && !mme_target.about_to_die && !mme_target.has_status_effect(Status_effect_code.EF_RESIST_POISON) && !mme_target.poison_resistance){
			                                        int poison_level;
			                                        if (mme_target.has_status_effect(Status_effect_code.EF_POISON))
			                                            poison_level = Math.min(3,mme_target.status_effects.get(Status_effect_code.EF_POISON).level + m.poison_melee);
			                                        else
			                                            poison_level = m.poison_melee;
			                                        mme_target.status_effects.put(Status_effect_code.EF_POISON,new Effect_parameters(10));
			                                        mme_target.status_effects.get(Status_effect_code.EF_POISON).level = poison_level;
			                                    }
			                                    // it might be a draining attack;
			                                    if (m.draining_melee && mme_target.alive && !mme_target.about_to_die && !mme_target.draining_resistance){
			                                    	boolean countered = false;
			                                    	for (Character c2 : Gamestate.p.chars){
			                                    		if (c2.alive && !c2.about_to_die && c2.current_stance!=null && c2.current_stance==Stance_code.ST_COUNTER_DRAINING){
			                                    			countered = true;
			                                    			c2.current_stance = null;
			                                    		}
			                                    	}
			                                    	if (!countered){
			                                    		mme_target.energy = Math.max(0,mme_target.energy - Gamestate.w.randint(5,10));
			                                    	}
			                                    }
			                                }
			                            } else {
			                                Gamestate.add_sound(m.melee_miss_sound);
			                            }
			                            continue;
			                        }
			                    }
			                }
			                // rear row mobs get their default missile attack
			                if (!m.front_row() && m.missile_attack){
			                    double mmi_period = m.missile_period;
			                    if (rallied)
			                    	mmi_period = mmi_period * 0.7;
			                    if (m.confused)
			                    	mmi_period = mmi_period * 3.0;
			                    if (m.stunned)
			                    	mmi_period = mmi_period * 99;
			                	if (m.slow_until > clock) mmi_period=mmi_period*2;
			                    if (one_in((int)Math.floor(mmi_period / elapsed_since_last_process))){
			                        Character mmi_target = Gamestate.p.random_front_target(m);
			                        if (mmi_target!=null){
			                            if (m.missile_hit_roll(mmi_target)){
			                                Gamestate.add_sound(m.missile_hit_sound);
			                                int damage = m.missile_damage_roll();
			                                if (damage > mmi_target.current_armor()){
			                                    m.flash_image = "pale";
			                                    mmi_target.take_damage(Math.max(0,damage-mmi_target.current_armor()));
			                                }
			                            } else {
			                                Gamestate.add_sound(m.missile_miss_sound);
			                            }
			                        }
			                        continue;
			                    }
			                }
			            }
		            }
		        }
		    }
		}

		int party_damage_in_last_few_seconds(double seconds){
			int damage=0;
			for (int i=0; i<party_damage_times.size(); i++){
				if (party_damage_times.get(i) >= (clock-seconds))
					damage = damage + party_damage_amounts.get(i);
			}
			return damage;
		}

		int mobs_damage_in_last_few_seconds(double seconds){
			int damage=0;
			for (int i=0; i<mob_damage_times.size(); i++){
				if (mob_damage_times.get(i) >= (clock-seconds))
					damage = damage + mob_damage_amounts.get(i);
			}
			return damage;
		}

		boolean check_can_flee(){
			for (Mob m : Gamestate.e.mobs){
				if (m.alive && !m.about_to_die && m.prevents_fleeing){
					Gamestate.add_message("You cannot flee from the " + m.name + "!",m.image,Color.WHITE);
					return false;
				}
			}
			return can_flee;
		}

		int randint(int a,int b){
			// inclusive
			return rand.nextInt(b-a+1)+a;
		}
		// or use Gamestate.w.rand.nextDouble()

		boolean one_in(int x){
		    if (x<=0){
		        return true;
		    } else {
		        return randint(1,x)==1;
		    }
		}

		int success_chance(int x){
		    if (x>=12)
		        return 100;
		    if (x==11)
		        return 99;
		    if (x==10)
		        return 97;
		    if (x==9)
		        return 95;
		    if (x==8)
		        return 90;
		    if (x==7)
		        return 70;
		    if (x==6)
		        return 50;
		    if (x==5)
		        return 30;
		    if (x==4)
		        return 10;
		    if (x==3)
		        return 5;
		    if (x==2)
		        return 3;
		    if (x==1)
		        return 1;
		    return 0;
		}
}

